import { BaseLoader } from '@cherrystudio/embedjs-interfaces'
import { cleanString } from '@cherrystudio/embedjs-utils'
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters'
import md5 from 'md5'
import { OfficeParserConfig, parseOfficeAsync } from 'officeparser'

/**
 * OD 文件类型枚举
 * 支持 ODT（文本）、ODS（表格）、ODP（演示文稿）
 */
export enum OdType {
  OdtLoader = 'OdtLoader',
  OdsLoader = 'OdsLoader',
  OdpLoader = 'OdpLoader',
  undefined = 'undefined'
}

/**
 * OpenDocument 文件加载器
 * 支持 ODT、ODS、ODP 文件的解析与分块
 */
export class OdLoader<OdType> extends BaseLoader<{ type: string }> {
  private readonly odType: OdType // 文件类型
  private readonly filePath: string // 文件路径
  private extractedText: string // 提取的纯文本内容
  private config: OfficeParserConfig // 解析配置

  /**
   * 构造函数
   * @param odType 文件类型（ODT/ODS/ODP）
   * @param filePath 文件路径
   * @param chunkSize 分块大小（可选，默认1000）
   * @param chunkOverlap 分块重叠（可选，默认0）
   */
  constructor({
    odType,
    filePath,
    chunkSize,
    chunkOverlap
  }: {
    odType: OdType
    filePath: string
    chunkSize?: number
    chunkOverlap?: number
  }) {
    // 调用父类构造函数，唯一ID为类型+文件MD5
    super(`${odType}_${md5(filePath)}`, { filePath }, chunkSize ?? 1000, chunkOverlap ?? 0)
    this.odType = odType
    this.filePath = filePath
    this.extractedText = ''
    this.config = {
      newlineDelimiter: ' ',
      ignoreNotes: false
    }
  }

  /**
   * 异步提取 ODT/ODS/ODP 文件中的纯文本内容
   */
  private async extractTextFromOdt() {
    try {
      // 使用 officeparser 解析文件，返回纯文本
      this.extractedText = await parseOfficeAsync(this.filePath, this.config)
    } catch (err) {
      console.error('odLoader error', err)
      throw err
    }
  }

  /**
   * 生成未过滤的文本块
   * 1. 若未提取文本则先提取
   * 2. 使用分块器按需分割文本
   * 3. 每个分块带有类型和来源元数据
   */
  override async *getUnfilteredChunks() {
    if (!this.extractedText) {
      await this.extractTextFromOdt()
    }
    const chunker = new RecursiveCharacterTextSplitter({
      chunkSize: this.chunkSize,
      chunkOverlap: this.chunkOverlap
    })

    const chunks = await chunker.splitText(cleanString(this.extractedText))

    for (const chunk of chunks) {
      yield {
        pageContent: chunk,
        metadata: {
          type: this.odType as string,
          source: this.filePath
        }
      }
    }
  }
}
